# -*- coding: utf-8 -*-
"""
    xfree.user.views
    ~~~~~~~~~~~~~~~~~~~~~
    文件描述：这个模块处理已登录用户的用户资料和用户设置。
    作者：xfree
    邮箱：cy_83k@qq.com
"""

import logging

# 导入attr库，用于创建类的属性
import attr
# 导入Flask相关模块
from flask import Blueprint, flash, redirect, request, url_for
from flask.views import MethodView
# 从flask_babelplus导入gettext并简写成_，用于处理多语言文本
from flask_babelplus import gettext as _
# 导入flask_login中的current_user和login_required，用于处理用户登录相关操作
from flask_login import current_user, login_required
# 导入HookimplMarker用于标记插件实现函数
from pluggy import HookimplMarker

# 导入用户模型类
from xfree.user.models import User
# 导入用于渲染模板和注册视图的辅助函数
from xfree.utils.helpers import register_view, render_template

# 从相对路径下的模块导入多个异常类和服务工厂函数
from ..core.exceptions import PersistenceError, StopValidation
from .services.factories import (
    change_details_form_factory,
    change_email_form_factory,
    change_password_form_factory,
    details_update_factory,
    email_update_handler,
    password_update_handler,
    settings_form_factory,
    settings_update_handler,
)

impl = HookimplMarker("xfree")

logger = logging.getLogger(__name__)


@attr.s(frozen=True, eq=False, order=False, hash=False, repr=True)
class UserSettings(MethodView):
    """
    用户设置视图类

    这个类用于处理用户的通用设置操作，包括获取设置页面、提交设置更改等。
    """
    # 使用attr.ib创建一个属性form，其默认值由settings_form_factory函数生成
    form = attr.ib(factory=settings_form_factory)
    # 使用attr.ib创建一个属性settings_update_handler，其默认值由settings_update_handler函数生成
    settings_update_handler = attr.ib(factory=settings_update_handler)

    # 应用login_required装饰器，确保只有登录用户能访问这些视图
    decorators = [login_required]

    def get(self):
        """
        处理GET请求，渲染用户设置页面
        :return: 渲染后的用户设置页面
        """
        return self.render()

    def post(self):
        """
        处理POST请求，用于提交用户设置更改
        如果表单验证通过，尝试应用设置更改。如果出现验证停止或持久化错误，
        则进行相应的错误处理并重新渲染页面或显示错误消息后重定向。
        :return: 根据操作结果，返回渲染后的页面或重定向
        """
        if self.form.validate_on_submit():
            try:
                self.settings_update_handler.apply_changeset(
                    current_user, self.form.as_change()
                )
            except StopValidation as e:
                self.form.populate_errors(e.reasons)
                return self.render()
            except PersistenceError:
                logger.exception("Error while updating user settings")
                flash(_("Error while updating user settings"), "danger")
                return self.redirect()

            flash(_("Settings updated."), "success")
            return self.redirect()
        return self.render()

    def render(self):
        """
        渲染用户设置页面模板
        :return: 渲染后的用户设置页面
        """
        return render_template("user/general_settings.html", form=self.form)

    def redirect(self):
        """
        重定向到用户设置页面
        :return: 重定向到用户设置页面
        """
        return redirect(url_for("user.settings"))


@attr.s(frozen=True, hash=False, eq=False, order=False, repr=True)
class ChangePassword(MethodView):
    """
    修改密码视图类

    这个类用于处理用户修改密码的操作，包括获取修改密码页面、提交密码更改等。
    """
    # 使用attr.ib创建一个属性form，其默认值由change_password_form_factory函数生成
    form = attr.ib(factory=change_password_form_factory)
    # 使用attr.ib创建一个属性password_update_handler，其默认值由password_update_handler函数生成
    password_update_handler = attr.ib(factory=password_update_handler)
    # 应用login_required装饰器，确保只有登录用户能访问这些视图
    decorators = [login_required]

    def get(self):
        """
        处理GET请求，渲染修改密码页面
        :return: 渲染后的修改密码页面
        """
        return self.render()

    def post(self):
        """
        处理POST请求，用于提交密码更改
        如果表单验证通过，尝试应用密码更改。如果出现验证停止或持久化错误，
        则进行相应的错误处理并重新渲染页面或显示错误消息后重定向。
        :return: 根据操作结果，返回渲染后的页面或重定向
        """
        if self.form.validate_on_submit():
            try:
                self.password_update_handler.apply_changeset(
                    current_user, self.form.as_change()
                )
            except StopValidation as e:
                self.form.populate_errors(e.reasons)
                return self.render()
            except PersistenceError:
                logger.exception("Error while changing password")
                flash(_("Error while changing password"), "danger")
                return self.redirect()

            flash(_("Password updated."), "success")
            return self.redirect()
        return self.render()

    def render(self):
        """
        渲染修改密码页面模板
        :return: 渲染后的修改密码页面
        """
        return render_template("user/change_password.html", form=self.form)

    def redirect(self):
        """
        重定向到修改密码页面
        :return: 重定向到修改密码页面
        """
        return redirect(url_for("user.change_password"))


@attr.s(frozen=True, eq=False, order=False, hash=False, repr=True)
class ChangeEmail(MethodView):
    """
    修改电子邮件视图类

    这个类用于处理用户修改电子邮件的操作，包括获取修改电子邮件页面、提交电子邮件更改等。
    """
    # 使用attr.ib创建一个属性form，其默认值由change_email_form_factory函数生成
    form = attr.ib(factory=change_email_form_factory)
    # 使用attr.ib创建一个属性update_email_handler，其默认值由email_update_handler函数生成
    update_email_handler = attr.ib(factory=email_update_handler)
    # 应用login_required装饰器，确保只有登录用户能访问这些视图
    decorators = [login_required]

    def get(self):
        """
        处理GET请求，渲染修改电子邮件页面
        :return: 渲染后的修改电子邮件页面
        """
        return self.render()

    def post(self):
        """
        处理POST请求，用于提交电子邮件更改
        如果表单验证通过，尝试应用电子邮件更改。如果出现验证停止或持久化错误，
        则进行相应的错误处理并重新渲染页面或显示错误消息后重定向。
        :return: 根据操作结果，返回渲染后的页面或重定向
        """
        if self.form.validate_on_submit():
            try:
                self.update_email_handler.apply_changeset(
                    current_user, self.form.as_change()
                )
            except StopValidation as e:
                self.form.populate_errors(e.reasons)
                return self.render()
            except PersistenceError:
                logger.exception("Error while updating email")
                flash(_("Error while updating email"), "danger")
                return self.redirect()

            flash(_("Email address updated."), "success")
            return self.redirect()
        return self.render()

    def render(self):
        """
        渲染修改电子邮件页面模板
        :return: 渲染后的修改电子邮件页面
        """
        return render_template("user/change_email.html", form=self.form)

    def redirect(self):
        """
        重定向到修改电子邮件页面
        :return: 重定向到修改电子邮件页面
        """
        return redirect(url_for("user.change_email"))


@attr.s(frozen=True, repr=True, eq=False, order=False, hash=False)
class ChangeUserDetails(MethodView):
    """
    修改用户详细信息视图类

    这个类用于处理用户修改详细信息的操作，包括获取修改详细信息页面、提交详细信息更改等。
    """
    # 使用attr.ib创建一个属性form，其默认值由change_details_form_factory函数生成
    form = attr.ib(factory=change_details_form_factory)
    # 使用attr.ib创建一个属性details_update_handler，其默认值由details_update_factory函数生成
    details_update_handler = attr.ib(factory=details_update_factory)
    # 应用login_required装饰器，确保只有登录用户能访问这些视图
    decorators = [login_required]

    def get(self):
        """
        处理GET请求，渲染修改用户详细信息页面
        :return: 渲染后的修改用户详细信息页面
        """
        return self.render()

    def post(self):
        """
        处理POST请求，用于提交用户详细信息更改
        如果表单验证通过，尝试应用详细信息更改。如果出现验证停止或持久化错误，
        则进行相应的错误处理并重新渲染页面或显示错误消息后重定向。
        :return: 根据操作结果，返回渲染后的页面或重定向
        """
        if self.form.validate_on_submit():
            try:
                self.details_update_handler.apply_changeset(
                    current_user, self.form.as_change()
                )
            except StopValidation as e:
                self.form.populate_errors(e.reasons)
                return self.render()
            except PersistenceError:
                logger.exception("Error while updating user details")
                flash(_("Error while updating user details"), "danger")
                return self.redirect()

            flash(_("User details updated."), "success")
            return self.redirect()
        return self.render()

    def render(self):
        """
        渲染修改用户详细信息页面模板
        :return: 渲染后的修改用户详细信息页面
        """
        return render_template("user/change_user_details.html", form=self.form)

    def redirect(self):
        """
        重定向到修改用户详细信息页面
        :return: 重定向到修改用户详细信息页面
        """
        return redirect(url_for("user.change_user_details"))


class AllUserTopics(MethodView):  # pragma: no cover
    """
    用户所有主题视图类

    这个类用于处理获取用户所有主题的操作。
    （这里的pragma: no cover可能表示这个类在测试覆盖范围之外）
    """
    def get(self, username):
        """
        处理GET请求，获取指定用户的所有主题并渲染页面
        :param username: 用户名称
        :return: 渲染后的包含用户所有主题的页面
        """
        page = request.args.get("page", 1, type=int)
        user = User.query.filter_by(username=username).first_or_404()
        topics = user.all_topics(page, current_user)
        return render_template("user/all_topics.html", user=user, topics=topics)


class AllUserPosts(MethodView):  # pragma: no cover
    """
    用户所有帖子视图类

    这个类用于处理获取用户所有帖子的操作。
    （这里的pragma: no cover可能表示这个类在测试覆盖范围之外）
    """
    def get(self, username):
        """
        处理GET请求，获取指定用户的所有帖子并渲染页面
        :param username: 用户名称
        :return: 渲染后的包含用户所有帖子的页面
        """
        page = request.args.get("page", 1, type=int)
        user = User.query.filter_by(username=username).first_or_404()
        posts = user.all_posts(page, current_user)
        return render_template("user/all_posts.html", user=user, posts=posts)


class UserProfile(MethodView):  # pragma: no cover
    """
    用户资料视图类

    这个类用于处理获取用户资料的操作。
    （这里的pragma: no cover可能表示这个类在测试覆盖范围之外）
    """
    def get(self, username):
        """
        处理GET请求，获取指定用户的资料并渲染页面
        :param username: 用户名称
        :return: 渲染后的包含用户资料的页面
        """
        user = User.query.filter_by(username=username).first_or_404()
        return render_template("user/profile.html", user=user)


@impl(tryfirst=True)
def xfree_load_blueprints(app):
    """
    这个函数用于加载用户相关的蓝图到Flask应用中

    通过创建一个名为"user"的蓝图，并将各个用户视图类注册到蓝图上，
    最后将蓝图注册到应用中，实现用户相关功能的路由。
    :param app: Flask应用对象
    :return: 无
    """
    user = Blueprint("user", __name__)
    register_view(
        user, routes=["/settings/email"], view_func=ChangeEmail.as_view("change_email")
    )
    register_view(
        user, routes=["/settings/general"], view_func=UserSettings.as_view("settings")
    )
    register_view(
        user,
        routes=["/settings/password"],
        view_func=ChangePassword.as_view("change_password"),
    )
    register_view(
        user,
        routes=["/settings/user-details"],
        view_func=ChangeUserDetails.as_view("change_user_details"),
    )
    register_view(
        user,
        routes=["/<username>/posts"],
        view_func=AllUserPosts.as_view("view_all_posts"),
    )
    register_view(
        user,
        routes=["/<username>/topics"],
        view_func=AllUserTopics.as_view("view_all_topics"),
    )

    register_view(
        user, routes=["/<username>"], view_func=UserProfile.as_view("profile")
    )

    app.register_blueprint(user, url_prefix=app.config["USER_URL_PREFIX"])